iT邦幫忙

2024 iThome 鐵人賽

DAY 25
0

這篇介紹一下 JS 中內建的日期物件 Date

JS 中的 Date 採用的紀錄方式是 Unix 時間戳
Unix 時間戳 是時間眾多紀錄方式中的其中一種,這種方式會以西元 1970/01/01 00:00:00 UTC 為原始點來紀錄時間。
以 JS 的實現而言,雖然命名叫 Date,時間紀錄的最小單位為 毫秒 而非僅到日期。(為求表述方便,後面如果我說日期物件就是指 Date)

let timestamp1 = 0;
let date1 = new Date(timestamp1);
console.log(date1.toUTCString());//"Thu, 01 Jan 1970 00:00:00 GMT"
let timestamp2 = 1;
let date2 = new Date(timestamp2);
console.log(date2.toUTCString());//"Thu, 01 Jan 1970 00:00:00 GMT"
//看不出差別是因為只差一毫秒,上面的表示方式看不到毫秒
console.log(date2 - date1);//1 相減可以得到時間戳差

透過這個系統,實際上 Date 物件內部是有個屬性以數字在儲存時間戳。
雖然這個內部屬性無法直接存取,但我們可以使用 Date.getTime() 方式來得到物件上的時間戳。

let now = new Date("2024-10-09");
console.log(now.getTime());//1728432000000
let nowByTimeStamp = new Date(1728432000000);
console.log(nowByTimeStamp);
//Wed Oct 09 2024 08:00:00 GMT+0800 (Taipei Standard Time)

同時,JS 的 Date 物件也支援負數時間戳,即以負數表示 1970/01/01 以前的時間。

let before1970 = -11131561920000;
let before1970Date = new Date(before1970);
console.log(before1970Date.toUTCString()); 
//"Mon, 03 Apr 1617 11:28:00 GMT"

至於建構 Date 物件的方法,就以 new Date() 方式來建構,允許傳入的參數非常多,詳情請見MDN

Date 物件只能使用 new Date() 建構方式來建立,如果是呼叫 Date(),會是嘗試將其轉為字串回傳,而非回傳 Date 物件。

console.log(typeof new Date("2024-10-09"));//"object"
console.log(typeof Date("2024-10-09"));//"string"

透過 Unix 時間戳紀錄時間的好處有以下三種:

  1. 以整數形式紀錄時間
    要計算時間差變得非常容易,只要當作兩個表示數字的時間戳在計算就好,想要計算相差時間,直接用數學相減就能得到毫秒差,想比較時間先後,比較運算子如 > < 都能使用,但比較相等的話要用 getTime() 後再比較,否則會變為物件的比較而通常不如預期。
  2. 跨平台的兼容性
    大部分程式語言都支持這樣的時間戳紀錄法,差別可能在於精度到秒、毫秒或更精細。
    但轉換起來相對方便。
  3. 時間表述帶時區基準
    時間戳以 UTC 為基準來表示時間,當有跨時區的應用場景,可以透過 UTC 的方式來表明各個時間在各個時區的計數,而不致混亂。

取得與設置物件內容

Date 物件並不提供透過 [] 的方式來訪問屬性,所有的訪問都基於提供的方法。
方法過多我就不一列出,請直接參考上面附上的 MDN 文件左側。
基本就是 getset 後面加上時間單位,命名都蠻直覺的。

時間格式的制定

每個國家甚至地區往往都有各自不同習慣的時間計數方式,這件事在國際交流時是相當麻煩的。
後來由 國際標準化組織(ISO) 推出了 ISO8601 來做為一個通用的時間表述標準。

日期表述為 YYYY-MM-DD
時間表述為 hh:mm:ss
時間加日期表述為 YYYY-MM-DD hh:mm:ss
對時區的表述 透過 ±hh:mm 來表述,比如在後方加上 +08:00 表示 UTC+8 時區的時間
僅表示標準的 UTC+0 可以簡寫做 Z。

特別提到這個標準是因為這個標準應用廣泛,開發者們應該多以習以為常,甚至可能沒意識到這是一個標準。
只要有提到 ISOString,指的就是符合這個 ISO8601 標準的時間表述字串。

字串永遠由 24 或 27 個字元組成,有兩種表述方式:
YYYY-MM-DDTHH:mm:ss.sssZ±YYYYYY-MM-DDTHH:mm:ss.sssZ

JS 中的日期上下限

JS 的日期有一些比較要注意的地方,如 monthIndex 的 1 月的值是 0(注意,不是 month,特指 monthIndex,用於建構 Date 物件)。
另外日期裡的各個位數是會有溢出時進位和不足時借位的狀況的(因為背後是時間戳表述,各個單位其實就是乘以對應的基數最後計算為一個時間戳整數,自然會有進位和借位的狀況),如 monthIndex 寫了 12,則年會 +1,而月變為一月。要注意各個單位是否有在各自的範圍內。

JS 的時間戳允許上下限為 -8,640,000,000,000,000 到 8,640,000,000,000,000。
若超出這個數字,內部計數會變為 NaN,而嘗試以字串顯示日期時則會得到 Invalid Date

let maxDate = new Date(8640000000000000);
let overMaxDate = new Date(8640000000000001);
console.log(maxDate.toISOString());//"+275760-09-13T00:00:00.000Z"
console.log(overMaxDate.getTime());//NaN
console.log(overMaxDate.toISOString());//Uncaught RangeError: Invalid time value"

時區的處理

儘管 JS 的日期物件能夠轉換為特定時區方式來顯示,但儲存時皆基於 UTC,也沒有特別好的轉換方式,比如常見時區的 PT,CT 等等,需要自己手動撰寫。
網路上有許多他人寫好的專門處理時間的函式庫,如果專案會頻繁使用到跨時區的時間,且希望儲存時間帶有時區表述能力,建議使用函式庫輔助,會在操作上更為便利。(如常見的 moment.js)
時間的處理上有很多細部的細節要注意,例如日光節約時間(夏令時間)、閏秒(JS 的 Date 物件並不表述閏秒)等等,要考慮自己的系統需要到多深的實作,否則連時間欄位都亂掉,系統肯定會一團混亂。


至此關於 JS 的日期物件大致介紹告一段落,記得並理解時間戳的概念,會對操作時間有很大的幫助。


上一篇
正規表達式(Regular Expression)
下一篇
模組(Module)的前世今生 - IIFE、CommonJS、AMD、UMD、CMD
系列文
Don't make JavaScript Just Surpise31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言